package model

import (
	"errors"
	"gitee.com/sdumeeting/sfu-server/pkg/media"
	"gitee.com/sdumeeting/sfu-server/pkg/util"
	"sync"

	"time"

	"github.com/pion/webrtc/v2"
)

type Meeting struct {
	id int

	// 会议内的用户
	users    map[int]*User
	userLock *sync.RWMutex

	// 会议内的发布者
	pubPeers    map[int]*media.WebRTCPeer
	pubPeerLock *sync.RWMutex

	// 会议内的订阅者
	subPeers    map[int]*media.WebRTCPeer
	subPeerLock *sync.RWMutex

	// 会议创建时间
	createdAt int64

	// 会议人数限制
	limit int

	// 会议模式
	pattern int

	// 是否有屏幕共享
	screen     bool
	screenLock *sync.Mutex
}

func NewMeeting(id, limit, pattern int) *Meeting {
	return &Meeting{
		id:          id,
		users:       make(map[int]*User),
		userLock:    new(sync.RWMutex),
		pubPeers:    make(map[int]*media.WebRTCPeer),
		pubPeerLock: new(sync.RWMutex),
		subPeers:    make(map[int]*media.WebRTCPeer),
		subPeerLock: new(sync.RWMutex),
		createdAt:   time.Now().Unix(),
		limit:       limit,
		pattern:     pattern,
		screen:      false,
		screenLock:  new(sync.Mutex),
	}
}

func (m *Meeting) GetId() int {
	return m.id
}

// GetUsers 获取所有用户
func (m *Meeting) GetUsers() map[int]*User {
	m.userLock.RLock()
	defer m.userLock.RUnlock()

	return m.users
}

// IsExpired 会议是否过期
// 过期条件：参会者数量为零，且创建时间超过一分钟
func (m *Meeting) IsExpired() bool {
	// 检查当前参会人数是否为 0
	if len(m.users) != 0 {
		return false
	}

	// 检查是否超时，Unix 时间戳的单位为秒
	if time.Now().Unix()-m.createdAt > 60 {
		return true
	}

	return false
}

// AddUser 添加用户
func (m *Meeting) AddUser(user *User) {
	m.userLock.Lock()
	defer m.userLock.Unlock()

	m.users[user.GetId()] = user
}

// GetUserById 获取用户
func (m *Meeting) GetUserById(userId int) *User {
	m.userLock.RLock()
	defer m.userLock.RUnlock()

	return m.users[userId]
}

// GetUserNum 获取当前参会者的数量
func (m *Meeting) GetUserNum() int {
	m.userLock.RLock()
	defer m.userLock.RUnlock()

	return len(m.users)
}

// DeleteUserById 删除用户
func (m *Meeting) DeleteUserById(userId int) {
	m.userLock.Lock()
	defer m.userLock.Unlock()

	delete(m.users, userId)
}

// GetAllUserInfos 获取用户 id 与昵称关系的对应表
func (m *Meeting) GetAllUserInfos() map[int]*UserInfo {
	m.userLock.RLock()
	defer m.userLock.RUnlock()

	userInfos := make(map[int]*UserInfo, 0)

	// 遍历会议内的所有用户，将用户 id 与昵称添加到表中
	for userId, user := range m.users {
		userInfos[userId] = &UserInfo{userId, user.name}
	}

	return userInfos
}

// AddPublisherById 添加发布者
func (m *Meeting) AddPublisherById(id int) {
	m.pubPeerLock.Lock()
	defer m.pubPeerLock.Unlock()

	if m.pubPeers[id] != nil {
		m.pubPeers[id].Stop()
	}

	m.pubPeers[id] = media.NewWebRTCPeer(id)
}

// GetPublisherById 获取发布者
func (m *Meeting) GetPublisherById(id int) *media.WebRTCPeer {
	m.pubPeerLock.Lock()
	defer m.pubPeerLock.Unlock()

	return m.pubPeers[id]
}

// GetAllPublisherIds 获取会议内的所有发布者的 id
func (m *Meeting) GetAllPublisherIds() []int {
	m.pubPeerLock.RLock()
	defer m.pubPeerLock.RUnlock()

	// 创建数组用于存储发布者 id
	pubIds := make([]int, 0)

	// 遍历所有发布者
	for pubId := range m.pubPeers {
		// 将发布者的 id 存放在数组中
		pubIds = append(pubIds, pubId)
	}

	return pubIds
}

// DeletePublisherById 删除发布者
func (m *Meeting) DeletePublisherById(id int) {
	m.pubPeerLock.Lock()
	defer m.pubPeerLock.Unlock()

	if m.pubPeers[id] != nil {
		if m.pubPeers[id].PC != nil {
			_ = m.pubPeers[id].PC.Close()
		}
		m.pubPeers[id].Stop()
	}

	delete(m.pubPeers, id)
}

// AddSubscriberById 添加订阅者
func (m *Meeting) AddSubscriberById(id int) {
	m.subPeerLock.Lock()
	defer m.subPeerLock.Unlock()

	if m.subPeers[id] != nil {
		m.subPeers[id].Stop()
	}

	m.subPeers[id] = media.NewWebRTCPeer(id)
}

// GetSubscriberById 获取订阅者
func (m *Meeting) GetSubscriberById(id int) *media.WebRTCPeer {
	m.subPeerLock.Lock()
	defer m.subPeerLock.Unlock()

	return m.subPeers[id]
}

// DeleteSubscriberById 删除订阅者
func (m *Meeting) DeleteSubscriberById(id int) {
	m.subPeerLock.Lock()
	defer m.subPeerLock.Unlock()

	if m.subPeers[id] != nil {
		if m.subPeers[id].PC != nil {
			_ = m.subPeers[id].PC.Close()
		}
		m.subPeers[id].Stop()
	}

	delete(m.subPeers, id)
}

func (m *Meeting) SendPLI(skipId int) {
	m.pubPeerLock.RLock()
	defer m.pubPeerLock.RUnlock()
	for k, v := range m.pubPeers {
		if k != skipId {
			v.SendPLI()
		}
	}
}

// SendToOthers 将消息发给会议内的其他用户
func (m *Meeting) SendToOthers(sender *User, messageType string, data map[string]interface{}) {
	message := map[string]interface{}{
		"type": messageType,
		"data": data,
	}

	for id, user := range m.users {
		if id != sender.GetId() {
			_ = user.Conn.Send(util.Marshal(message))
		}
	}
}

// Broadcast 向会议内的所有成员广播消息
func (m *Meeting) Broadcast(messageType string, data map[string]interface{}) {
	// 根据消息格式生成消息
	message := map[string]interface{}{
		"type": messageType,
		"data": data,
	}

	m.userLock.RLock()
	defer m.userLock.RUnlock()

	// 遍历会议内所有用户，发送消息
	for _, user := range m.users {
		_ = user.Conn.Send(util.Marshal(message))
	}
}

// AnswerPublisher 响应发布者
func (m *Meeting) AnswerPublisher(id int, offer webrtc.SessionDescription) (
	webrtc.SessionDescription, error) {

	p := m.GetPublisherById(id)

	return p.AnswerPublisher(offer)
}

// AnswerSubscriber 响应订阅者
func (m *Meeting) AnswerSubscriber(id int, pubId int, offer webrtc.SessionDescription) (
	webrtc.SessionDescription, error) {

	p := m.GetSubscriberById(id)

	m.pubPeerLock.RLock()
	publisher := m.pubPeers[pubId]
	m.pubPeerLock.RUnlock()

	ticker := time.NewTicker(time.Millisecond * 2000)

	for {
		select {
		case <-ticker.C:
			return p.AnswerSubscriber(offer, &publisher.VideoTrack, &publisher.AudioTrack)
		default:
			if publisher.VideoTrack == nil || publisher.AudioTrack == nil {
				time.Sleep(time.Millisecond * 100)
			} else {
				return p.AnswerSubscriber(offer, &publisher.VideoTrack, &publisher.AudioTrack)
			}
		}
	}
}

// StartScreenSharing 启动屏幕共享
func (m *Meeting) StartScreenSharing() error {
	m.screenLock.Lock()
	defer m.screenLock.Unlock()

	// 如果当前屏幕共享已被占用，则返回错误
	if m.screen {
		return errors.New("screen share is occupied")
	}

	// 占用屏幕共享
	m.screen = true
	return nil
}

// EndScreenSharing 关闭屏幕共享
func (m *Meeting) EndScreenSharing() error {
	m.screenLock.Lock()
	defer m.screenLock.Unlock()

	// 如果当前屏幕共享未被启动，返回错误
	if !m.screen {
		return errors.New("screen share is not occupied")
	}

	// 关闭屏幕共享
	m.screen = false
	return nil
}

func (m *Meeting) Close() {
	m.pubPeerLock.Lock()
	defer m.pubPeerLock.Unlock()

	for _, v := range m.pubPeers {
		if v != nil {
			v.Stop()
			if v.PC != nil {
				_ = v.PC.Close()
			}
		}
	}
}
